package math;

import java.util.Collections;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

public class RuleSet {

  private HashMap<Class<?>, List<Rule>> rules;

  public RuleSet(Rule... rules) {
    this.rules = new HashMap<Class<?>, List<Rule>>();
    for (Rule rule: rules)
      addRule(rule);
  }

  public void addRule(Rule rule) {
    Expression template = rule.getTemplate();
    Class<?> type = template.getClass();
    List<Rule> rulesList = rules.get(type);
    if (rulesList == null) {
      rulesList = new ArrayList<Rule>();
      rules.put(type, rulesList);
    }
    rulesList.add(rule);
    Collections.sort(rulesList, new RuleSorter());
    System.out.println(rulesList);
  }

  public List<Expression> apply(Expression expr) {
    List<Expression> result = new ArrayList<Expression>();
    List<Expression> tmp = new LinkedList<Expression>();
    tmp.add(expr);
    int min = expr.countMembers();
    while (!tmp.isEmpty()) {
      Expression ae = tmp.remove(0);
      Class<?> type = ae.getClass();

      result.add(ae);
      List<Rule> possibleRules = rules.get(type);
      if (possibleRules != null) {
        for (Rule rule : possibleRules) {
          if (rule.matches(ae)) {
            Expression probablyNew = rule.apply(expr);
            if (!result.contains(probablyNew) && !tmp.contains(probablyNew) && probablyNew.countMembers() <= min) {
              min = probablyNew.countMembers();
              tmp.add(probablyNew);
            }
          }
        }
      }
    }
    if (min < expr.countMembers()){
//      System.out.println(min + " " + expr.countMembers() + " " + expr);
      for (int i = result.size()-1; i>=0; i--){
        if (result.get(i).countMembers() > min)
          result.remove(i);
      }
        
      
    }
    return result;
  }
  
  static class RuleSorter implements Comparator<Rule>{

    @Override
    public int compare(Rule r1, Rule r2) {
      int s1 = r1.getTemplate().countMembers();
      int s2 = r2.getTemplate().countMembers();
      int d1 = r1.getTarget().countMembers();
      int d2 = r2.getTarget().countMembers();
      int diff1 = d1 - s1;
      int diff2 = d2 - s2;
      if (diff1 != diff2)
        return diff1 - diff2;
      if (s1 != s2)
        return s2-s1;
      return 0;
    }
    
  }
}
